npm 私有化仓库方案 Verdaccio
为什么需要 npm 私有仓库
前端团队在开发过程中,经常会产出可复用的组件库、工具函数、业务模块等。这些内部包不适合发布到 npm 公网,但又需要在团队内部共享,此时就需要搭建 npm 私有仓库。
截至当前,Verdaccio 最新稳定版为 6.7.0(支持 Node.js 24,ESM + CJS 双构建,Docker 支持完善)。Verdaccio 7 正在开发中。
npm 私有仓库方案对比
| 方案 | 适用场景 | 部署难度 | 特色 |
|---|---|---|---|
| Verdaccio | 前端/Node.js 项目 | 极低 | 轻量、零配置、插件丰富 |
| Nexus | 前端 + 后端(Java) | 中等 | 支持 Maven/npm/Docker 等多格式 |
| CNPM | 淘宝系/大规模 | 较高 | 淘宝 npm 源同款方案 |
| JFrog Artifactory | 企业级通用 | 高 | 全语言制品管理,企业版功能全 |
| GitHub Packages | 开源/小型团队 | 低 | 与 GitHub 仓库集成,国内访问可能受限 |
JFrog Artifactory 社区版仅支持 Java 制品(jar/war/pom),npm/Docker 需购买企业版或专业版,且社区版限制 10 用户 + 2GB 存储。
国内团队还需注意:GitHub Packages 在国内可能因网络问题导致
npm install失败,建议优先选择自建方案(Verdaccio)或国内镜像源方案。
Verdaccio 快速入门
安装与启动
# 全局安装(需 Node.js >= 18,推荐 v20+)
npm install -g verdaccio
# 也支持 pnpm 安装
pnpm install -g verdaccio
# 启动
verdaccio
bash
默认运行在 http://localhost:4873,配置文件位于 ~/.config/verdaccio/config.yaml。
版本要求注意:Verdaccio 6.x 要求 Node.js >= 18。如果使用更早的 Node 版本,请安装 Verdaccio 5.x(
npm install -g verdaccio@5)。
Docker 部署(推荐生产使用)
# docker-compose.yml
version: "3.8"
services:
verdaccio:
image: verdaccio/verdaccio:6
container_name: verdaccio
ports:
- "4873:4873"
volumes:
- ./conf:/verdaccio/conf
- ./storage:/verdaccio/storage
- ./plugins:/verdaccio/plugins
environment:
- VERDACCIO_PUBLIC_URL=http://your-server:4873
restart: unless-stopped
yaml
注意:生产环境建议使用 bind mount(如上所示)替代 named volume,便于直接查看和备份配置文件及存储数据。
启动:
docker compose up -d
bash
需要预先创建配置文件:
# 创建目录
mkdir -p conf storage plugins
# 生成默认配置
docker run --rm -v $(pwd)/conf:/verdaccio/conf verdaccio/verdaccio:6 sh -c "cp /verdaccio/conf/default.yaml /verdaccio/conf/config.yaml 2>/dev/null || true"
# 如果默认配置未生成,手动创建最小配置
cat > conf/config.yaml << 'EOF'
storage: /verdaccio/storage
plugins: /verdaccio/plugins
web:
title: 私有 NPM 仓库
auth:
htpasswd:
file: /verdaccio/conf/htpasswd
max_users: -1
uplinks:
npmjs:
url: https://registry.npmmirror.com/
cache: true
packages:
"@*/*":
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: npmjs
"**":
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: npmjs
listen: 0.0.0.0:4873
logs:
- { type: stdout, format: pretty, level: http }
EOF
# 设置目录权限(Docker 容器内使用 verdaccio 用户,uid 10001)
chown -R 10001:10001 conf storage plugins
bash
配置 NPM 客户端
使用 nrm 管理 Registry 源
nrm(NPM Registry Manager)可以方便地在多个 registry 源之间切换:
# 安装 nrm
npm install -g nrm
# 查看所有可用源
nrm ls
# 添加私有源
nrm add private http://localhost:4873
# 切换到私有源
nrm use private
# 切换回淘宝源
nrm use taobao
# 切换到 npm 官方源
nrm use npm
# 测试各源响应速度
nrm test
bash
使用 .npmrc 精细控制源
除了全局切换,还可以通过 .npmrc 实现更精细的控制:
项目级配置(推荐):在项目根目录创建 .npmrc:
# .npmrc(项目级)
# 默认使用私有仓库
registry=http://localhost:4873
# 特定 scope 始终走私有仓库
@my-company:registry=http://localhost:4873
# 认证 token(通过 npm login 后自动生成)
//localhost:4873/:_authToken="xxxx-xxxx-xxxx-xxxx"
ini
用户级配置:~/.npmrc:
# 全局默认源
registry=https://registry.npmmirror.com/
# 公司 scope 走私有仓库
@my-company:registry=http://npm.example.com:4873/
//npm.example.com:4873/:_authToken="${NPM_TOKEN}"
ini
公共源与私有源的切换策略
| 策略 | 配置方式 | 适用场景 |
|---|---|---|
| 全部走私有仓库 | nrm use private | 私有仓库配置了 uplink 代理,公共包自动缓存 |
| 特定 scope 走私有 | .npmrc 中设置 @scope:registry | 只管理内部包,其他走公共源 |
| 按项目区分 | 项目级 .npmrc | 不同项目使用不同私有仓库 |
| CI/CD 环境 | 环境变量 NPM_CONFIG_REGISTRY | 流水线中动态指定 |
最佳实践:推荐使用"全部走私有仓库"策略。Verdaccio 的 uplink 机制会自动代理公共包并缓存,无需担心公共包的获取问题。这样团队所有依赖都经过统一的仓库入口,便于审计和离线使用。
用户注册与登录
# 添加用户(首次使用)
npm adduser --registry http://localhost:4873
# 按提示输入用户名、密码、邮箱
# 登录
npm login --registry http://localhost:4873
# 查看当前登录状态
npm whoami --registry http://localhost:4873
bash
发布与使用私有包
创建并发布包
# 初始化一个测试模块
mkdir my-lib && cd my-lib
npm init -y
# 编辑 package.json,确认 name 和 version
bash
创建入口文件:
// index.js
module.exports = {
hello: function (name) {
return `Hello, ${name}!`;
},
};
js
发布:
npm publish
bash
发布成功后,访问 http://localhost:4873 即可在 Web UI 中看到刚发布的包。
发布 Scoped 包(推荐)
为避免包名冲突,推荐使用 @scope 前缀发布内部包:
# 初始化 scoped 包
mkdir @my-company/ui-lib && cd @my-company/ui-lib
npm init --scope=@my-company -y
bash
生成的 package.json:
{
"name": "@my-company/ui-lib",
"version": "1.0.0",
"main": "index.js"
}
json
发布:
# scoped 包默认为私有,需显式指定公开访问(如果需要)
npm publish --access public
# 或保持私有(仅认证用户可安装)
npm publish
bash
版本管理与更新
# 查看当前包的所有版本
npm view my-lib versions --registry http://localhost:4873
# 使用 npm version 管理版本号
npm version patch # 1.0.0 → 1.0.1(修复)
npm version minor # 1.0.1 → 1.1.0(新功能)
npm version major # 1.1.0 → 2.0.0(破坏性变更)
# 重新发布
npm publish
# 删除某个版本(需要 unpublish 权限)
npm unpublish my-lib@1.0.0 --registry http://localhost:4873
# 删除整个包(24小时内可完全删除)
npm unpublish my-lib --force --registry http://localhost:4873
bash
安装私有包
# 直接安装
npm install my-lib
# 安装指定版本
npm install my-lib@1.2.0
# 安装 scoped 包
npm install @my-company/ui-lib
bash
核心配置详解
Verdaccio 的配置文件为 config.yaml,位于 ~/.config/verdaccio/config.yaml(Docker 部署时在 /verdaccio/conf/config.yaml)。
完整配置示例
# ~/.config/verdaccio/config.yaml
# 存储路径
storage: /verdaccio/storage
plugins: /verdaccio/plugins
# Web UI 配置
web:
title: 私有 NPM 仓库
# 认证配置
auth:
htpasswd:
file: ./htpasswd
max_users: -1 # -1 表示不限制用户数
# 上游代理配置(核心功能)
uplinks:
npmjs:
url: https://registry.npmjs.org/
cache: true # 开启缓存
# 使用淘宝源加速
taobao:
url: https://registry.npmmirror.com/
cache: true
# 包访问权限
packages:
# 私有包:仅团队内部可发布
"my-company-*":
access: $authenticated
publish: $authenticated
unpublish: $authenticated
# 公共包:代理到上游
"@*/*":
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: taobao npmjs
"**":
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: taobao npmjs
# 服务器配置
server:
keepAliveTimeout: 60
# 监听配置
listen: 0.0.0.0:4873
# 日志配置
logs:
- { type: stdout, format: pretty, level: http }
yaml
Uplink 缓存策略
Uplink 是 Verdaccio 的代理机制,当本地不存在某个包时,会自动从上游仓库拉取:
uplinks:
npmjs:
url: https://registry.npmjs.org/
cache: true # 缓存 tarball 到本地 storage
maxage: 30m # 元数据缓存时间
timeout: 30s # 请求超时
max_fails: 5 # 最大失败次数
fail_timeout: 10m # 失败后冷却时间
yaml
| 配置项 | 说明 |
|---|---|
cache: true | 缓存 tarball 文件到 storage 目录 |
cache: false | 仅缓存元数据,不缓存 tarball |
maxage | 包元数据的缓存有效期 |
验证缓存效果
# 清除本地 npm 缓存
npm cache clean -f
# 删除 node_modules
rm -rf node_modules
# 安装一个公共包(首次从上游拉取,自动缓存)
npm install react vue
# 查看 storage 目录
ls ~/.config/verdaccio/storage/
# 可以看到 react、vue 等包的 .tgz 文件
# 再次安装时,直接从本地缓存返回
bash
Packages 权限控制
packages:
# 匹配模式支持通配符
"my-team-*":
access: $all # 所有人可读
publish: $authenticated # 认证用户可发布
proxy: npmjs # 代理到上游
"@my-scope/*":
access: $authenticated
publish: admin user1 # 指定用户可发布
yaml
| 权限值 | 含义 |
|---|---|
$all | 所有人(含匿名) |
$authenticated | 已认证用户 |
$anonymous | 匿名用户 |
| 具体用户名 | 指定用户 |
PM2 进程管理
生产环境建议使用 PM2 管理 Verdaccio 进程:
# 安装 PM2
npm install -g pm2
# 使用 PM2 启动 Verdaccio
pm2 start verdaccio --name npm-registry
# 设置开机自启
pm2 startup
pm2 save
# 查看状态
pm2 status
# 查看日志
pm2 logs npm-registry
bash
SSL 证书配置
通过反向代理(Nginx)启用 HTTPS:
# /etc/nginx/conf.d/verdaccio.conf
server {
listen 443 ssl;
server_name npm.example.com;
ssl_certificate /etc/nginx/ssl/npm.example.com.crt;
ssl_certificate_key /etc/nginx/ssl/npm.example.com.key;
# 最大包体限制(npm 包可能较大)
client_max_body_size 100m;
location / {
proxy_pass http://127.0.0.1:4873;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 支持 npm publish 的大文件上传
proxy_request_buffering off;
}
}
nginx
配合 Let's Encrypt 自动续期:
# 安装 certbot
apt install certbot python3-certbot-nginx
# 申请证书
certbot --nginx -d npm.example.com
# 自动续期(certbot 默认已配置定时任务)
certbot renew --dry-run
bash
CI/CD 集成
将 Verdaccio 接入 CI/CD 流水线,可以实现包的自动发布和版本管理。
GitHub Actions 集成
方案一:使用官方 Action(推荐)
# .github/workflows/publish.yml
name: Publish to Private Registry
on:
push:
branches: [main]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Start Verdaccio
uses: verdaccio/github-action@v1
with:
verdaccio-version: '6.x'
- name: Install dependencies
run: npm ci
- name: Authenticate & Publish
run: |
npx npm-cli-adduser \
--registry http://localhost:4873 \
--username ci-bot \
--password ${{ secrets.REGISTRY_PASSWORD }} \
--email ci@example.com
npm publish --registry http://localhost:4873
yaml
方案二:使用 NPM_TOKEN 认证
# .github/workflows/publish.yml
name: Publish Package
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://npm.example.com'
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
yaml
GitLab CI 集成
# .gitlab-ci.yml
stages:
- publish
publish_npm:
stage: publish
image: node:20
services:
- name: verdaccio/verdaccio:6
alias: verdaccio
variables:
npm_config_registry: "http://verdaccio:4873"
script:
- npm ci --registry http://verdaccio:4873
- npx npm-cli-adduser
--registry http://verdaccio:4873
--username ci-bot
--password $REGISTRY_PASSWORD
--email ci@example.com
- npm publish --registry http://verdaccio:4873
only:
- main
yaml
生成 NPM_TOKEN
# 在 Verdaccio 上为 CI 创建专用 token
npm token create --registry http://localhost:4873
# 或通过 curl 直接获取
curl -XPUT -H "Content-type: application/json" \
-d '{"name": "ci-bot", "password": "your-password"}' \
http://localhost:4873/-/user/org.couchdb.user:ci-bot
# 返回的 token 即为 NPM_TOKEN,配置到 CI 的 secrets 中
bash
与 Monorepo 配合使用
在 pnpm workspace / Turborepo 等 monorepo 方案中,Verdaccio 可作为本地发布测试的中间仓库:
# 在 monorepo 根目录的 .npmrc 中配置
@my-company:registry=http://localhost:4873
//localhost:4873/:_authToken=${NPM_TOKEN}
bash
# Turborepo 中发布所有包
turbo run build --filter="./packages/*"
turbo run publish --filter="./packages/*"
# 或手动逐包发布
pnpm --filter @my-company/ui-lib publish --registry http://localhost:4873 --no-git-checks
bash
本地测试技巧:在将包发布到公共 npm 之前,先发布到本地 Verdaccio 进行集成测试,验证其他项目能否正常安装和使用。
典型工作流
开发者本地
│
├── npm install react ──→ Verdaccio ──→ 淘宝源/npmjs(缓存)
│ │
├── npm publish my-lib ──→ Verdaccio(存储)
│ │
└── npm install my-lib ──→ Verdaccio(本地直接返回)
text
- 团队成员发布内部包到 Verdaccio
- 其他成员通过 Verdaccio 安装内部包
- 公共包自动代理并缓存,减少重复下载
- 不方便开源的模块安全存储在私有仓库中
小结
| 特性 | Verdaccio |
|---|---|
| 零配置启动 | verdaccio 一行命令 |
| 轻量级 | 纯 Node.js,资源占用少 |
| 代理缓存 | 自动代理上游并缓存 |
| 权限控制 | 基于用户/包名的粒度控制 |
| Web UI | 内置包浏览界面 |
| 插件系统 | 支持自定义认证、存储插件 |
| Docker 支持 | 官方维护镜像 |
Verdaccio 是前端团队搭建 npm 私有仓库的首选方案,部署简单,功能实用。对于需要管理多种语言制品的团队,可以考虑 Nexus 或 JFrog Artifactory。
常见问题与故障排查
1. 发布失败:403 Forbidden
# 检查是否已登录
npm whoami --registry http://localhost:4873
# 重新登录
npm login --registry http://localhost:4873
# 检查 config.yaml 中 publish 权限配置
# 确保 publish: $authenticated 或指定了正确的用户名
bash
2. 缓存未生效,storage 中没有 .tgz 文件
# 1. 确认 uplink 中 cache 设置为 true
# 2. 清除本地 npm 缓存
npm cache clean -f
# 3. 删除 node_modules 重新安装
rm -rf node_modules
npm install
# 4. 检查 storage 目录权限
ls -la ~/.config/verdaccio/storage/
bash
3. Docker 部署后无法访问
# 检查容器状态
docker ps -a
# 查看容器日志
docker logs verdaccio
# 常见原因:目录权限不正确
# Verdaccio 容器使用 uid 10001 运行
chown -R 10001:10001 storage/ conf/ plugins/
# 检查端口映射
docker port verdaccio
bash
4. nrm 切换源后仍然使用旧源
# 确认当前使用的源
npm config get registry
# 如果显示不正确,手动设置
npm config set registry http://localhost:4873
# 或在项目根目录创建 .npmrc 覆盖全局设置
echo "registry=http://localhost:4873" > .npmrc
bash
5. 包已存在无法重新发布
# 方案一:升级版本号后重新发布
npm version patch
npm publish
# 方案二:删除旧版本
npm unpublish my-lib@1.0.0 --registry http://localhost:4873 --force
# 方案三:直接删除 storage 中的包目录
rm -rf ~/.config/verdaccio/storage/my-lib
# 然后重启 Verdaccio
bash
6. 私有包意外被代理到公共源
在 config.yaml 中确保私有包的匹配规则在通配规则之前:
packages:
# 私有包规则必须在前
"my-company-*":
access: $authenticated
publish: $authenticated
# 注意:私有包不设置 proxy,避免被代理
"@my-scope/*":
access: $authenticated
publish: $authenticated
# 公共包通配规则在后
"**":
access: $all
publish: $authenticated
proxy: npmjs
yaml
参考资源
↑